package com.udream.support;

import java.io.File;
import java.io.InputStream;
import java.util.Date;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.FileInfo;
import com.qiniu.util.Auth;

import jodd.io.StreamUtil;

/**
 * <p>
 * QiniuKit.简化<a href="https://github.com/qiniu/java-sdk">七牛API</a>
 * </p>
 * <p>
 * 增加配置Configuration
 * </p>
 * 
 * <pre>
 * public void uploadImage() {
 *  // what?
 *  String domain = "http://XXXXXXXXXXXXXXXXXXXXXXXX.qiniucdn.com";
 *  String accessKey = "XXXXXXXXXXXXXXXXXXXXXXXX";
 *  String secretKey = "XXXXXXXXXXXXXXXXXXXXXXXX";
 *  String bucketName = "XXXXXXXXXXXXXXXXXXXXXXXX";
 * 
 *  // that
 *  QiniuSupport qiniu = new QiniuSupport();
 *  qiniu.setAccessKey(accessKey);
 *  qiniu.setSecretKey(secretKey);
 *  qiniu.setBucketName(bucketName);
 *  qiniu.setDomain(domain);
 * 
 *  // put
 *  String key = "data/web/picture/tespic/base/CmsContent/0015c8a34dd54bb08447f835ad250f45.jpg";
 *  File imageFile = new File(key);
 *  QiniuPutResponse putResponse = qiniu.put(imageFile, key);
 *  Assert.assertTrue(putResponse.isSuccess());
 *  Assert.assertEquals(key, putResponse.getKey());
 *  Assert.assertTrue(qiniu.existed(key));
 *  Assert.assertEquals(
 *   "http://XXXXXXXXXXXXXXXXXXXXXXXX.qiniucdn.com/data/web/picture/tespic/base/CmsContent/0015c8a34dd54bb08447f835ad250f45.jpg",
 *   qiniu.getInternetUrl(key));
 * 
 * }
 * </pre>
 * 
 * @author zhoupan
 * 
 */
public class QiniuKit {

 /** The Constant SEPARATOR. */
 public static final String SEPARATOR = "/";

 /**
  * Builds the virtual path.
  *
  * @param parts
  *         the parts
  * @return the string
  */
 public static String buildVirtualPath(String... parts) {
  StringBuilder path = new StringBuilder();
  if (parts != null) {
   for (int i = 0; i < parts.length; i++) {
    if (i > 0 && !StringUtils.endsWith(path.toString(), SEPARATOR)) {
     path.append(SEPARATOR);
    }
    String part = parts[i];
    part = StringUtils.replace(part, "\\", SEPARATOR);
    if (StringUtils.startsWith(part, SEPARATOR)) {
     part = StringUtils.substringAfter(part, SEPARATOR);
    }
    if (StringUtils.isNotBlank(part)) {
     path.append(part);
    }
   }
  }
  return path.toString();
 }

 /**
  * QiniuPutResponse.
  */
 public static class QiniuPutResponse {

  /** The hash. */
  private String hash;

  /** The key. */
  private String key;

  /** The error. */
  private String error;

  /** The success. */
  private boolean success;

  /**
   * Gets the hash.
   *
   * @return the hash
   */
  public String getHash() {
   return hash;
  }

  /**
   * Sets the hash.
   *
   * @param hash
   *         the hash
   */
  public void setHash(String hash) {
   this.hash = hash;
  }

  /**
   * Gets the key.
   *
   * @return the key
   */
  public String getKey() {
   return key;
  }

  /**
   * Sets the key.
   *
   * @param key
   *         the key
   */
  public void setKey(String key) {
   this.key = key;
  }

  /**
   * Gets the error.
   *
   * @return the error
   */
  public String getError() {
   return error;
  }

  /**
   * Sets the error.
   *
   * @param error
   *         the error
   */
  public void setError(String error) {
   this.error = error;
  }

  /**
   * Checks if is success.
   *
   * @return true, if checks if is success
   */
  public boolean isSuccess() {
   return success;
  }

  /**
   * Sets the success.
   *
   * @param success
   *         the success
   */
  public void setSuccess(boolean success) {
   this.success = success;
  }

  /**
   * On exception.
   *
   * @param e
   *         the e
   */
  public void onException(Throwable e) {
   e.printStackTrace();
   this.onException(e.getMessage());
  }

  /**
   * On exception.
   *
   * @param error
   *         the error
   */
  public void onException(String error) {
   this.success = false;
   this.error = error;
  }
 }

 /**
  * QiniuSupport.
  */
 public static class QiniuSupport {

  /** The access key. */
  protected String accessKey;

  /** The secret key. */
  protected String secretKey;

  /** The bucket name. */
  protected String bucketName;

  /** The domain. */
  protected String domain;

  /** The bucket manager. */
  protected BucketManager bucketManager;

  /** The configuration. */
  protected Configuration configuration;

  /**
   * Gets the configuration.
   *
   * @return the configuration
   */
  public Configuration getConfiguration() {
   if (this.configuration == null) {
    // 不设置Zone.
    this.configuration = new Configuration();
   }
   return this.configuration;
  }

  /**
   * Gets the bucket manager.
   *
   * @return the bucket manager
   */
  public BucketManager getBucketManager() {
   if (bucketManager == null) {
    this.bucketManager = new BucketManager(this.auth(), this.getConfiguration());
   }
   return bucketManager;
  }

  /**
   * Sets the bucket manager.
   *
   * @param bucketManager
   *         the bucket manager
   */
  public void setBucketManager(BucketManager bucketManager) {
   this.bucketManager = bucketManager;
  }

  /** The upload manager. */
  UploadManager uploadManager;

  /**
   * Gets the upload manager.
   *
   * @return the upload manager
   */
  public UploadManager getUploadManager() {
   if (this.uploadManager == null) {
    this.uploadManager = new UploadManager(this.getConfiguration());
   }
   return uploadManager;
  }

  /**
   * Sets the upload manager.
   *
   * @param uploadManager
   *         the upload manager
   */
  public void setUploadManager(UploadManager uploadManager) {
   this.uploadManager = uploadManager;
  }

  /**
   * Auth.
   *
   * @return the auth
   */
  public Auth auth() {
   return Auth.create(accessKey, secretKey);
  }

  /**
   * Upload token.
   *
   * @return the string
   */
  public String uploadToken() {
   return auth().uploadToken(this.getBucketName());
  }

  /**
   * Upload token.
   *
   * @param key
   *         the key
   * @return the string
   */
  public String uploadToken(String key) {
   return auth().uploadToken(this.getBucketName(), key);
  }

  /**
   * Put.
   *
   * @param data
   *         the data
   * @param key
   *         the key
   * @return the qiniu put response
   */
  public QiniuPutResponse put(byte[] data, String key) {
   QiniuPutResponse response = new QiniuPutResponse();
   try {
    Response uploadResponse = this.getUploadManager().put(data, resolveVirtualPath(key), this.uploadToken(key));
    QiniuPutResponse putResponse = this.fromJson(uploadResponse.bodyString(), QiniuPutResponse.class);
    putResponse.setSuccess(uploadResponse.isOK());
    return putResponse;
   } catch (QiniuException qe) {
    LogUtils.performance.error("qiniuException:{}", qe.error());
    response.onException(qe.error());
   } catch (Throwable e) {
    response.onException(e);
   }
   return response;
  }

  /**
   * Put.
   *
   * @param data
   *         the data
   * @param key
   *         the key
   * @return the qiniu put response
   */
  public QiniuPutResponse put(File data, String key) {
   QiniuPutResponse response = new QiniuPutResponse();
   try {
    Response uploadResponse = this.getUploadManager().put(data, resolveVirtualPath(key), this.uploadToken(key));
    QiniuPutResponse putResponse = this.fromJson(uploadResponse.bodyString(), QiniuPutResponse.class);
    putResponse.setSuccess(uploadResponse.isOK());
    return putResponse;
   } catch (QiniuException qe) {
    LogUtils.performance.error("qiniuException:{}", qe.error());
    try {
     LogUtils.performance.error("qiniuException:{}", qe.response.bodyString());
    } catch (QiniuException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
    response.onException(qe.error());
   } catch (Throwable e) {
    response.onException(e);
   }
   return response;
  }

  /**
   * Put.
   *
   * @param data
   *         the data
   * @param key
   *         the key
   * @return the qiniu put response
   */
  public QiniuPutResponse put(InputStream data, String key) {
   QiniuPutResponse response = new QiniuPutResponse();
   try {
    return put(StreamUtil.readBytes(data), resolveVirtualPath(key));
   } catch (QiniuException qe) {
    LogUtils.performance.error("qiniuException:{}", qe.error());
    response.onException(qe.error());
   } catch (Throwable e) {
    response.onException(e);
   }
   return response;
  }

  /**
   * Existed.
   *
   * @param key
   *         the key
   * @return true, if existed
   */
  public boolean existed(String key) {
   try {
    FileInfo info = this.getBucketManager().stat(this.getBucketName(), resolveVirtualPath(key));
    return info.hash != null;
   } catch (QiniuException qe) {
    LogUtils.performance.error("qiniuException:{}", qe.error());
    return false;
   } catch (Throwable e) {
    return false;
   }
  }

  /**
   * Delete.
   *
   * @param key
   *         the key
   * @return true, if delete
   */
  public boolean delete(String key) {
   try {
    this.getBucketManager().delete(this.getBucketName(), resolveVirtualPath(key));
    return true;
   } catch (QiniuException qe) {
    LogUtils.performance.error("qiniuException:{}", qe.error());
    return false;
   } catch (Throwable e) {
    return false;
   }
  }

  /**
   * Resolve virtual path.
   *
   * @param key
   *         the key
   * @return the string
   */
  public String resolveVirtualPath(String key) {
   key = this.resolveKey(key);
   return buildVirtualPath(key);
  }

  /**
   * Gets the access key.
   *
   * @return the access key
   */
  public String getAccessKey() {
   return accessKey;
  }

  /**
   * Sets the access key.
   *
   * @param accessKey
   *         the access key
   */
  public void setAccessKey(String accessKey) {
   this.accessKey = accessKey;
  }

  /**
   * Gets the secret key.
   *
   * @return the secret key
   */
  public String getSecretKey() {
   return secretKey;
  }

  /**
   * Sets the secret key.
   *
   * @param secretKey
   *         the secret key
   */
  public void setSecretKey(String secretKey) {
   this.secretKey = secretKey;
  }

  /**
   * Gets the bucket name.
   *
   * @return the bucket name
   */
  public String getBucketName() {
   return bucketName;
  }

  /**
   * Sets the bucket name.
   *
   * @param bucketName
   *         the bucket name
   */
  public void setBucketName(String bucketName) {
   this.bucketName = bucketName;
  }

  /**
   * Gets the domain.
   *
   * @return the domain
   */
  public String getDomain() {
   return domain;
  }

  /**
   * Sets the domain.
   *
   * @param domain
   *         the domain
   */
  public void setDomain(String domain) {
   this.domain = domain;
  }

  /** The gson. */
  Gson gson = new Gson();

  /**
   * To json.
   *
   * @param obj
   *         the obj
   * @return the string
   */
  public String toJson(Object obj) {
   return gson.toJson(obj);
  }

  /**
   * From json.
   *
   * @param <T>
   *         the generic type
   * @param json
   *         the json
   * @param classOfT
   *         the class of t
   * @return the t
   */
  public <T> T fromJson(String json, Class<T> classOfT) {
   return gson.fromJson(json, classOfT);
  }

  /**
   * Checks if is internet url.
   *
   * @param key
   *         the key
   * @return true, if checks if is internet url
   */
  public boolean isInternetUrl(String key) {
   if (StringUtils.contains(key, ":")) {
    return true;
   }
   if (StringUtils.contains(key, this.getDomain())) {
    return true;
   }
   return false;
  }

  /**
   * Resolve key.
   *
   * @param url
   *         the url
   * @return the string
   */
  public String resolveKey(String url) {
   if (this.isInternetUrl(url)) {
    String prefix = buildVirtualPath(this.getDomain());
    String key = StringUtils.substringAfter(url, prefix);
    if (StringUtils.isBlank(key)) {
     // throw new RuntimeException("resolve key from " + url + " failed.");
     LogUtils.performance.error("resolve key from {} failed. just return it.", url);
     return url;
    }
    LogUtils.performance.debug("resolve key={} from url={} success.", key, url);
    return key;
   }
   return url;
  }

  /**
   * Gets the internet url.
   *
   * @param key
   *         the key
   * @return the internet url
   */
  public String getInternetUrl(String key) {
   //如果包含协议例如:http://,https://不添加域名.
   if (CommonSupport.contains(key, ":")) {
    return key;
   }
   key = this.resolveKey(key);
   return buildVirtualPath(this.getDomain(), key);
  }

  /**
   * Gets the path.
   *
   * @param key
   *         the key
   * @return the path
   */
  public String getPath(String key) {
   key = this.resolveKey(key);
   return buildVirtualPath("/", key);
  }

  /**
   * Generate uuid key.
   *
   * @param key
   *         the key
   * @return the string
   */
  public String generateUuidKey(String key) {
   String yearMonth = DateFormatUtils.format(new Date(), "yyyy/MM/");
   String uuid = java.util.UUID.randomUUID().toString().replaceAll("-", "");
   String ext = FilenameUtils.getExtension(key);
   if (StringUtils.isBlank(ext)) {
    return String.format("%s%s", yearMonth, uuid);
   } else {
    return String.format("%s%s.%s", yearMonth, uuid, ext);
   }
  }
 }
}
